/*
	动态申请设备号
	实现文件操作接口
	动态创建设备文件
	容错处理
	面向对象的封装
	
GPIO1_C7 计算引脚号	55	    用这个引脚测试

还有一个引脚：

GPIO0_B5   计算引脚号	13 默认给是CPU的指示灯， 如果要控制，在设备树里面把

kernel-rockchip-nanopi4-linux-v4.4.y/arch/arm64/boot/dts/rockchip$ vi rk3399-nanopi4-common.dtsi +396


注释掉即可

leds: gpio-leds {
		compatible = "gpio-leds";
		pinctrl-names = "default";
		pinctrl-0 =<&leds_gpio>;

		led@1 {
			gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
			label = "status_led";
			linux,default-trigger = "heartbeat";
			linux,default-trigger-delay-ms = <0>;
		};
	};

	 目的：  gpiolib+ 私有数据 拉IO口


*/


#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h> 										   
#include <asm/uaccess.h>

#include <linux/string.h>     //内核中的strcmp 函数


//定义led的设备结构体(类)
struct rk_led {
	unsigned int major;
    struct class *cls;
    struct device *dev;
	unsigned int gpio_led; //保存引脚号
	char kbuf[100];
};



static struct rk_led *led_device;



//交给驱动框架来调用的回调函数
static int led_open (struct inode *inode, struct file *filp)
{
	//gpio_direction_output(led_device->gpio_led, 1); //led on
	printk("<kernel>call %s()\n", __func__); 
	return 0;
}

static int led_close (struct inode *inode, struct file *filp)
{
	//gpio_direction_output(led_device->gpio_led, 0); //led off
	printk("<kernel>call %s()\n", __func__); 
	return 0;

}



ssize_t  hello_read (struct file *filp, char __user *buf, size_t size, loff_t *flag)
{
	int ret;
	char *data = "i come from kernel space";
	int len = strlen(data);
	ret = copy_to_user(buf, data, len);
	if (ret) {
		printk("copy_to_user fail\n");
		return -EFAULT;

	}
	printk("<kernel>call %s() len=%d\n", __func__, len); 
	return len;
}


ssize_t hello_write (struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
	int ret;


	memset(led_device->kbuf, 0, sizeof(led_device->kbuf));
	
	ret = copy_from_user(led_device->kbuf, buf,  size); //拷贝到内核空间				
	if (ret) {
		printk("copy_from_user fail\n");
		return -EFAULT;
	}
	printk("<kernel>size=%ld read from user space: %s\n", size, led_device->kbuf);
	printk("<kernel>call %s()\n", __func__); 

     
    if (!strcmp(led_device->kbuf, "on"))
	{
		 gpio_direction_output(led_device->gpio_led, 1);
	}
	else if (!strcmp(led_device->kbuf, "off"))
	{
		 gpio_direction_output(led_device->gpio_led, 0);
	}


	
	return size;
	
}



static struct file_operations fops = { //只部分初始化自己用的
	.owner = THIS_MODULE, //当前模块绑定
	.open = led_open,
	.release = led_close,
	.read = hello_read,
	.write = hello_write,

};

static int __init led_init(void)
{
	int ret;
	//创建led设备对象

	led_device = kmalloc(sizeof(struct rk_led), GFP_KERNEL);
	if (!led_device) {
		printk("kmalloc fail\n");
		return -ENOMEM;
	}
	

	//动态申请设备号
	led_device->major = register_chrdev(0, "led_drv", &fops);
	if (led_device->major < 0) {
		printk("<kernel>register_chrdev fail\n");
		ret = -EBUSY;
		goto err_register_chrdev;

	}
	printk("<kernel> major=%d\n", led_device->major);


	//创建设备类

	led_device->cls = class_create(THIS_MODULE, "led_cls");
	if (IS_ERR(led_device->cls)) {			
		printk("couldn't create class\n");	
		
		ret = PTR_ERR(led_device->cls);	
		goto err_class_create;
	}			
	

	//创建设备对象==创建设备文件
	led_device->dev = device_create(led_device->cls, NULL, MKDEV(led_device->major, 0), NULL,  "led"); // /dev/led
	if (IS_ERR(led_device->dev)) { 											
		printk("device_create fail\n");
		ret = PTR_ERR(led_device->dev); 	
		goto err_device_create;
	}


	//硬件初始化
	led_device->gpio_led = 55;  //GPIO1_C7 计算引脚号	55	

	//申请引脚
	ret = gpio_request(led_device->gpio_led, "led2_green");
	if (ret) {
		printk("gpio_request fail\n");
		goto err_gpio_request;

	}
	//设置引脚为输出功能,默认低电平
	gpio_direction_output(led_device->gpio_led, 0);

	
	
	printk("<kernel>call %s()\n", __func__);  
	return 0;

//集中错误处理
err_gpio_request:
	device_destroy(led_device->cls, MKDEV(led_device->major, 0));
err_device_create:
	class_destroy(led_device->cls);
err_class_create:
	unregister_chrdev(led_device->major, "led_drv");
err_register_chrdev:
	kfree(led_device);
	return ret;

	
}


static void __exit led_exit(void)
{
 	//释放引脚
	gpio_free(led_device->gpio_led);
	device_destroy(led_device->cls, MKDEV(led_device->major, 0));
	class_destroy(led_device->cls);
	unregister_chrdev(led_device->major, "led_drv");
	kfree(led_device);
	printk("<kernel>call %s()for oop @ %d\n", __FUNCTION__, __LINE__);  
	
}

module_init(led_init); 
module_exit(led_exit); 
MODULE_LICENSE("GPL"); 


